package engine.systems.render.camera
{
	import ash.tools.ListIteratingSystem;
	
	import engine.GameEngine;
	import engine.classes.enumerations.SystemPriority;
	import engine.managers.ScreenManager;
	
	import flash.display.Stage;
	
	/**
	 * The Camera System correctly positions the Render display after all Spatial Offsets have been applied.
	 * This also resizes the Rectangle viewport and background Display Object Container for Camera viewport
	 * and off-screen Sleep space checks.
	 * 
	 * @see Camera
	 * @see CameraNode
	 */
	public class CameraSystem extends ListIteratingSystem
	{
		private const _stage:Stage = GameEngine.screenManager.stage;
		
		public function CameraSystem()
		{
			super(CameraNode, updateNode, nodeAdded, nodeRemoved);
			
			this.priority = SystemPriority.CAMERA;
		}
		
		public function updateNode(node:CameraNode, time:Number):void
		{
			var x:Number 		= node.spatial.x;
			var y:Number 		= node.spatial.y;
			var scaleX:Number 	= node.spatial.scaleX;
			var scaleY:Number 	= node.spatial.scaleY;
			
			if(node.offset)
			{
				x 		+= node.offset.x;
				y 		+= node.offset.y;
				scaleX 	+= node.offset.scaleX;
				scaleY 	+= node.offset.scaleY;
			}
			
			/**
			 * "Moving" the Camera actually means moving the Render display the Camera's associated with.
			 * This means moving the display in the OPPOSITE direction to simulate movement in the right
			 * direction by flipping where the Spatial actually put the display.
			 */
			node.render.display.x 		= -x;
			node.render.display.y 		= -y;
			node.render.display.scaleX 	= scaleX;
			node.render.display.scaleY 	= scaleY;
			
			//The display needs to be moved to compensate for changes in scale.
			node.render.display.x -= x * (node.render.display.scaleX - 1);
			node.render.display.y -= y * (node.render.display.scaleY - 1);
			
			var centerX:Number = _stage.stageWidth * 0.5;
			var centerY:Number = _stage.stageHeight * 0.5;
			
			//The display also needs to be offset by the center of the Camera.
			node.render.display.x += centerX;
			node.render.display.y += centerY;
			
			//Scaling the Spatial up/down will cause the viewport to get smaller/bigger.
			var viewportSizeX:Number = centerX * (1 / node.render.display.scaleX);
			var viewportSizeY:Number = centerY * (1 / node.render.display.scaleY);
			
			/**
			 * The viewport is the viewable area that Spatials reference to keep things on-screen.
			 * 
			 * Recalculate the current viewport Rectangle based on the Camera's current Spatial.
			 */
			node.camera._viewport.left 		= x - viewportSizeX;
			node.camera._viewport.right 	= x + viewportSizeX;
			
			node.camera._viewport.top 		= y - viewportSizeY;
			node.camera._viewport.bottom 	= y + viewportSizeY;
			
			var bounds:Number
			if(node.camera.bounds)
			{
				if(node.camera._viewport.left < node.camera.bounds.left)
				{
					bounds = node.camera.bounds.left - node.camera._viewport.left;
					node.render.display.x 	-= bounds;
					node.spatial.x 			+= bounds;
				}
				else if(node.camera._viewport.right > node.camera.bounds.right)
				{
					bounds = node.camera.bounds.right - node.camera._viewport.right;
					node.render.display.x 	-= bounds;
					node.spatial.x 			+= bounds;
				}
				
				if(node.camera._viewport.top < node.camera.bounds.top)
				{
					bounds = node.camera.bounds.top - node.camera._viewport.top;
					node.render.display.y 	-= bounds;
					node.spatial.y 			+= bounds;
				}
				else if(node.camera._viewport.bottom > node.camera.bounds.bottom)
				{
					bounds = node.camera.bounds.bottom - node.camera._viewport.bottom;
					node.render.display.y 	-= bounds;
					node.spatial.y 			+= bounds;
				}
			}
		}
		
		private function nodeAdded(node:CameraNode):void
		{
			/**
			 * When the Camera is removed, it shouldn't be removing its display from its parent.
			 * This would cause the Group Container to be removed from the display list, and nothing
			 * in the scene would exist anymore.
			 */
			node.render.locked = true;
			node.render.remove = false;
		}
		
		private function nodeRemoved(node:CameraNode):void
		{
			/**
			 * The Group Container the Camera's using should revert to its original values should the
			 * Camera be removed.
			 */
			node.render.display.x 			= 0;
			node.render.display.y 			= 0;
			node.render.display.rotation 	= 0;
			node.render.display.scaleX 		= 1;
			node.render.display.scaleY 		= 1;
		}
	}
}